<!doctype html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>Goldfish Scheme REPL</title>
    <link
      href="https://fonts.googleapis.com/css?family=Noto+Sans+SC"
      rel="stylesheet"
    />
    <link
      rel="stylesheet"
      href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/codemirror.min.css"
    />
    <link
      rel="stylesheet"
      href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/theme/dracula.min.css"
    />
    <style>
      html {
        font-family: "Open Sans", sans-serif;
      }
      body {
        background: #f8f9fa;
      }
      .repl-container {
        margin: 20px auto;
        max-width: 900px;
        background: white;
        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
        padding: 20px;
        border-radius: 8px;
        position: relative;
      }
      .repl-entry {
        margin-bottom: 25px;
        border: 1px solid #e0e0e0;
        padding: 15px;
        border-radius: 6px;
        background: #ffffff;
        position: relative;
      }
      .entry-number {
        position: absolute;
        top: 18px;
        left: -19px;
        background: #f1f1f1;
        color: #6272a4;
        font-weight: bold;
        border-radius: 3px;
        padding: 2px 8px;
        font-size: 13px;
        z-index: 2;
        user-select: none;
        border: 1px solid #e0e0e0;
      }
      .input-area {
        width: 100%;
        margin-bottom: 15px;
      }
      .CodeMirror {
        height: auto;
        border-radius: 4px;
        margin-bottom: 10px;
        border: 1px solid #e0e0e0;
      }
      .output-area {
        background: #282a36;
        padding: 15px;
        border-radius: 4px;
        white-space: pre-wrap;
        display: none;
        font-family: "Fira Code", monospace;
        margin-top: 10px;
      }
      .error {
        color: #ff5555;
      }
      .stdout {
        color: #8be9fd;
      }
      .result {
        color: #50fa7b;
      }
      button {
        padding: 8px 16px;
        background: #6272a4;
        color: white;
        border: none;
        border-radius: 4px;
        cursor: pointer;
        transition: background 0.2s;
        font-size: 14px;
      }
      button:hover {
        background: #44475a;
      }
      button:disabled {
        background: #999;
        cursor: not-allowed;
      }
      h1 {
        color: #44475a;
        margin-bottom: 15px;
        text-align: center;
      }
      .shortcuts-hint {
        text-align: center;
        margin-bottom: 20px;
        padding: 10px;
        background: #f8f9fa;
        border-radius: 6px;
        font-size: 13px;
        color: #666;
        border: 1px solid #e0e0e0;
      }
      .shortcuts-hint span {
        margin: 0 5px;
        padding: 2px 6px;
        background: #fff;
        border-radius: 3px;
        border: 1px solid #ddd;
      }
      .history-panel {
        position: fixed;
        right: 20px;
        top: 20px;
        width: 250px;
        height: calc(50vh - 100px);
        background: #f7f7fa;
        border-left: 1px solid #e0e0e0;
        border-radius: 8px 8px 0 0;
        box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
        padding: 16px 16px 16px 16px;
        overflow-y: auto;
        font-size: 14px;
        z-index: 1000;
      }
      
      .favorites-panel {
        position: fixed;
        right: 20px;
        top: calc(50vh + 20px);
        width: 250px;
        height: calc(50vh - 100px);
        background: #f7f7fa;
        border-left: 1px solid #e0e0e0;
        border-radius: 0 0 8px 8px;
        box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
        padding: 16px 16px 16px 16px;
        overflow-y: auto;
        font-size: 14px;
        z-index: 1000;
      }
      .history-panel h2,
      .favorites-panel h2 {
        font-size: 16px;
        color: #44475a;
        margin: 0 0 10px 0;
        text-align: left;
      }
      .history-list,
      .favorites-list {
        list-style: none;
        padding: 0;
        margin: 0;
      }
      .history-item,
      .favorites-item {
        background: #fff;
        border: 1px solid #e0e0e0;
        border-radius: 4px;
        margin-bottom: 8px;
        padding: 6px 8px;
        cursor: pointer;
        transition: background 0.2s;
        white-space: pre-wrap;
        word-break: break-all;
      }
      .history-item:hover,
      .favorites-item:hover {
        background: #e9e9f7;
      }
      .cm-error-highlight {
        background: #800020;
        border-bottom: 2px solid #ff5555;
      }
      .CodeMirror-lint-marker-error {
        background: none !important;
        color: #ff5555 !important;
        font-size: 18px;
        top: 2px;
      }
      .CodeMirror-lint-tooltip {
        background: #fff0f0;
        color: #b22222;
        border: 1px solid #ff5555;
        border-radius: 4px;
        font-size: 14px;
        padding: 6px 10px;
        box-shadow: 0 2px 8px rgba(255, 85, 85, 0.15);
      }
      .history-actions {
        position: fixed;
        top: 20px;
        left: 20px;
        display: flex;
        gap: 8px;
        z-index: 1001;
      }
      .history-actions button {
        flex: 0 1 auto;
        font-size: 13px;
        padding: 4px 8px;
      }

      /* 移动端适配样式 */
      @media (max-width: 768px) {
        .shortcuts-hint {
          display: none;
        }

        .repl-container {
          margin: 10px;
          padding: 15px;
          border-radius: 6px;
        }
        
        h1 {
          font-size: 20px;
          margin-bottom: 10px;
        }
        
        .repl-entry {
          margin-bottom: 20px;
          padding: 12px;
        }
        
        .entry-number {
          position: static;
          display: inline-block;
          margin-bottom: 8px;
          font-size: 12px;
          padding: 1px 6px;
        }
        
        .input-area {
          margin-bottom: 12px;
        }
        
        .CodeMirror {
          font-size: 14px;
          line-height: 1.4;
        }
        
        .output-area {
          padding: 12px;
          font-size: 13px;
          line-height: 1.3;
        }

        #clear-history,
        #export-history,
        #import-history,
        #share-history {
          display: none;
        }
        
        .history-panel,
        .favorites-panel {
          display: none;
        }

        .shortcut-hint {
          display: none;
        }
      }
    </style>
  </head>
  <body>
    <div class="repl-container">
      <h1>Goldfish Scheme REPL</h1>
      <div class="shortcuts-hint">
        <span>Tab: 自动补全</span> • <span>Ctrl+Enter: 求值</span> •
        <span>Ctrl+A: 全选</span> • <span>Ctrl+/: 注释</span> •
        <span>↑↓: 历史记录</span>
      </div>
      <div id="repl-entries"></div>
      <div class="repl-entry">
        <div class="input-area">
          <textarea placeholder="输入Scheme代码..."></textarea>
          <button class="eval-button">求值 (Ctrl+Enter)</button>
        </div>
        <div class="output-area"></div>
      </div>
      <div class="history-actions">
        <button id="clear-history">清空历史</button>
        <button id="export-history">导出历史</button>
        <button id="import-history">导入历史</button>
        <button id="share-history">分享历史</button>
        <input type="file" id="import-file" style="display:none" accept=".json"/>
      </div>
      <div class="history-panel" id="history-panel">
        <h2>历史记录</h2>
        <ul class="history-list" id="history-list"></ul>
      </div>
      <div class="favorites-panel" id="favorites-panel">
        <h2>收藏夹</h2>
        <ul class="favorites-list" id="favorites-list"></ul>
      </div>
    </div>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/codemirror.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/mode/scheme/scheme.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/addon/edit/matchbrackets.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/addon/edit/closebrackets.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/addon/edit/continue-list.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/addon/comment/comment.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.2/addon/selection/active-line.min.js"></script>
    <script src="goldfish_repl_wasm.js"></script>
    <script>
      (function () {
        // ---- 历史管理 ----
        const History = {
          data: [],
          pointer: null,
          add(item) {
            if (
              item &&
              (this.data.length === 0 ||
                this.data[this.data.length - 1] !== item)
            ) {
              this.data.push(item);
              this.saveToLocal();
            }
            this.pointer = null;
          },
          prev() {
            if (this.data.length === 0) return null;
            if (this.pointer === null) this.pointer = this.data.length - 1;
            else if (this.pointer > 0) this.pointer--;
            return this.data[this.pointer];
          },
          next() {
            if (this.data.length === 0 || this.pointer === null) return "";
            if (this.pointer < this.data.length - 1) {
              this.pointer++;
              return this.data[this.pointer];
            } else {
              this.pointer = null;
              return "";
            }
          },
          render(panel, onSelect) {
            panel.innerHTML = "";
            this.data
              .slice()
              .reverse()
              .forEach((item) => {
                const li = document.createElement("li");
                li.className = "history-item";
                li.style.display = "flex";
                li.style.justifyContent = "space-between";
                li.style.alignItems = "center";
                
                const content = document.createElement("div");
                content.textContent = item;
                content.title = "点击回填到输入区";
                content.style.flex = "1";
                content.style.cursor = "pointer";
                content.addEventListener("click", () => onSelect(item));
                
                const favoriteBtn = document.createElement("button");
                favoriteBtn.style.cssText = `
                  background: #6272a4;
                  color: white;
                  border: none;
                  border-radius: 3px;
                  padding: 2px 6px;
                  font-size: 11px;
                  cursor: pointer;
                  margin-left: 8px;
                  min-width: 32px;
                  opacity: 0;
                  transition: all 0.2s;
                `;
                favoriteBtn.textContent = Favorites.has(item) ? "★" : "☆";
                favoriteBtn.title = Favorites.has(item) ? "取消收藏" : "添加到收藏";
                favoriteBtn.addEventListener("click", (e) => {
                  e.stopPropagation();
                  if (Favorites.has(item)) {
                    Favorites.remove(item);
                    favoriteBtn.textContent = "☆";
                    favoriteBtn.title = "添加到收藏";
                  } else {
                    Favorites.add(item);
                    favoriteBtn.textContent = "★";
                    favoriteBtn.title = "取消收藏";
                  }
                  REPL.updateFavoritesPanel();
                });
                
                li.appendChild(content);
                li.appendChild(favoriteBtn);
                
                li.addEventListener('mouseenter', () => {
                  favoriteBtn.style.opacity = '1';
                });
                li.addEventListener('mouseleave', () => {
                  favoriteBtn.style.opacity = '0';
                });
                
                panel.appendChild(li);
              });
          },
        };

        // 收藏管理
        const Favorites = {
          data: [],
          
          add(item) {
            if (item && !this.data.includes(item)) {
              this.data.push(item);
              this.saveToLocal();
              return true;
            }
            return false;
          },
          
          remove(item) {
            const index = this.data.indexOf(item);
            if (index > -1) {
              this.data.splice(index, 1);
              this.saveToLocal();
              return true;
            }
            return false;
          },
          
          has(item) {
            return this.data.includes(item);
          },
          
          saveToLocal() {
            try {
              localStorage.setItem('goldfish_repl_favorites', JSON.stringify(this.data));
              return true;
            } catch (e) {
              console.error('保存收藏失败:', e);
              return false;
            }
          },
          
          loadFromLocal() {
            try {
              const saved = localStorage.getItem('goldfish_repl_favorites');
              if (saved) {
                const arr = JSON.parse(saved);
                if (Array.isArray(arr)) {
                  this.data = arr;
                  return true;
                }
              }
            } catch (e) {
              console.error('加载收藏失败:', e);
            }
            return false;
          },
          
          render(panel, onSelect) {
            panel.innerHTML = "";
            this.data
              .slice()
              .reverse()
              .forEach((item) => {
                const li = document.createElement("li");
                li.className = "favorites-item";
                li.style.display = "flex";
                li.style.justifyContent = "space-between";
                li.style.alignItems = "center";
                
                const content = document.createElement("div");
                content.textContent = item;
                content.title = "点击回填到输入区";
                content.style.flex = "1";
                content.style.cursor = "pointer";
                content.addEventListener("click", () => onSelect(item));
                
                const removeBtn = document.createElement("button");
                removeBtn.style.cssText = `
                  background: #6272a4;
                  color: white;
                  border: none;
                  border-radius: 3px;
                  padding: 2px 6px;
                  font-size: 11px;
                  cursor: pointer;
                  margin-left: 8px;
                  min-width: 32px;
                  opacity: 0;
                  transition: all 0.2s;
                `;
                removeBtn.textContent = "删除";
                removeBtn.title = "从收藏中删除";
                removeBtn.addEventListener("click", (e) => {
                  e.stopPropagation();
                  Favorites.remove(item);
                  REPL.updateFavoritesPanel();
                  REPL.updateHistoryPanel();
                });
                
                li.appendChild(content);
                li.appendChild(removeBtn);
                
                li.addEventListener('mouseenter', () => {
                  removeBtn.style.opacity = '1';
                });
                li.addEventListener('mouseleave', () => {
                  removeBtn.style.opacity = '0';
                });
                
                panel.appendChild(li);
              });
          }
        };

        // 清空历史
        History.clear = function() {
          this.data = [];
          this.pointer = null;
          try {
            localStorage.removeItem('goldfish_repl_history');
          } catch (e) {
            console.error('清空本地存储失败:', e);
          }
        };

        // 导出历史
        History.export = function() {
          return JSON.stringify(this.data, null, 2);
        };

        // 导入历史
        History.import = function(json) {
          try {
            const arr = JSON.parse(json);
            if (Array.isArray(arr)) {
              this.data = arr;
              this.pointer = null;
              return true;
            }
          } catch (e) {}
          return false;
        };

        // 保存到本地存储
        History.saveToLocal = function() {
          try {
            localStorage.setItem('goldfish_repl_history', JSON.stringify(this.data));
            return true;
          } catch (e) {
            console.error('保存历史失败:', e);
            return false;
          }
        };

        // 从本地存储加载
        History.loadFromLocal = function() {
          try {
            const saved = localStorage.getItem('goldfish_repl_history');
            if (saved) {
              const arr = JSON.parse(saved);
              if (Array.isArray(arr)) {
                this.data = arr;
                this.pointer = null;
                return true;
              }
            }
          } catch (e) {
            console.error('加载历史失败:', e);
          }
          return false;
        };

        // 生成分享链接
        History.generateShareLink = function() {
          try {
            const data = JSON.stringify(this.data);
            const encoded = btoa(unescape(encodeURIComponent(data)));
            const url = new URL(window.location.href);
            url.searchParams.set('history', encoded);
            return url.toString();
          } catch (e) {
            console.error('生成分享链接失败:', e);
            return null;
          }
        };

        // 从URL参数加载历史
        History.loadFromURL = function() {
          try {
            const url = new URL(window.location.href);
            const encoded = url.searchParams.get('history');
            if (encoded) {
              const data = decodeURIComponent(escape(atob(encoded)));
              const arr = JSON.parse(data);
              if (Array.isArray(arr)) {
                this.data = arr;
                this.pointer = null;
                return true;
              }
            }
          } catch (e) {
            console.error('从URL加载历史失败:', e);
          }
          return false;
        };

        // ---- CodeMirror 配置 ----
        // Scheme关键词列表
        const schemeKeywords = {
          // 基本定义和绑定
          definitions: [
            "define",
            "define-syntax",
            "define-macro",
            "define-values",
            "define-record-type",
          ],
          // 函数和表达式
          functions: [
            "lambda",
            "lambda*",
            "define*",
            "define-public",
            "define-private",
          ],
          // 局部绑定
          bindings: [
            "let",
            "let*",
            "letrec",
            "letrec*",
            "let-values",
            "let*-values",
          ],
          // 控制流
          control: [
            "if",
            "cond",
            "else",
            "when",
            "unless",
            "case",
            "case-lambda",
          ],
          // 序列和块
          blocks: ["begin", "begin0", "do", "delay", "force"],
          // 赋值
          assignment: ["set!", "set-car!", "set-cdr!"],
          // 引用
          quoting: ["quote", "quasiquote", "unquote", "unquote-splicing"],
          // 列表操作
          listOps: [
            "car",
            "cdr",
            "cons",
            "list",
            "append",
            "reverse",
            "length",
            "null?",
            "list?",
          ],
          // 高阶函数
          higherOrder: [
            "map",
            "apply",
            "fold",
            "fold-right",
            "filter",
            "for-each",
          ],
          // 算术运算
          arithmetic: [
            "+",
            "-",
            "*",
            "/",
            "modulo",
            "remainder",
            "quotient",
            "expt",
            "sqrt",
          ],
          // 比较运算
          comparison: ["=", "<", ">", "<=", ">=", "eq?", "eqv?", "equal?"],
          // 类型检查
          predicates: [
            "number?",
            "string?",
            "symbol?",
            "boolean?",
            "pair?",
            "vector?",
            "procedure?",
          ],
          // 输入输出
          io: ["display", "newline", "write", "read", "print", "format"],
          // 逻辑运算
          logic: ["and", "or", "not", "xor"],
          // 字符串操作
          string: [
            "string-append",
            "string-length",
            "string-ref",
            "substring",
            "string->symbol",
            "symbol->string",
          ],
          // 向量操作
          vector: [
            "vector",
            "make-vector",
            "vector-ref",
            "vector-set!",
            "vector-length",
          ],
          // 错误处理
          error: ["error", "with-error-handler", "guard"],
          // 其他常用
          misc: ["eval", "load", "require", "provide", "include", "include-ci"],
        };

        const allKeywords = Object.values(schemeKeywords).flat();

        function getCurrentWord(cm) {
          const cur = cm.getCursor();
          const line = cm.getLine(cur.line);
          const textBefore = line.slice(0, cur.ch);

          let wordStart = cur.ch;
          while (wordStart > 0) {
            const char = textBefore[wordStart - 1];
            if (/[\s\(\)\[\]{}\"\']/.test(char)) {
              break;
            }
            wordStart--;
          }
          return textBefore.slice(wordStart);
        }

        function createEditor(
          textarea,
          onEval,
          onChange,
          onFocus,
          onHistoryNav,
        ) {
          const editor = CodeMirror.fromTextArea(textarea, {
            mode: "scheme",
            theme: "dracula",
            lineNumbers: true,
            lineWrapping: true,
            viewportMargin: Infinity,
            matchBrackets: true,
            autoCloseBrackets: true,
            indentUnit: 2,
            smartIndent: true,
            electricChars: true,
            indentWithTabs: false,
            styleActiveLine: true,
            tabSize: 2,
            autofocus: true,
            extraKeys: {
              Tab: function (cm) {
                const currentWord = getCurrentWord(cm);
                if (currentWord.length >= 2) {
                  const matches = allKeywords.filter((k) =>
                    k.startsWith(currentWord),
                  );
                  if (matches.length > 0) {
                    // 找到所有匹配项的共同前缀
                    let commonPrefix = matches[0];
                    for (let i = 1; i < matches.length; i++) {
                      const match = matches[i];
                      let j = 0;
                      while (
                        j < commonPrefix.length &&
                        j < match.length &&
                        commonPrefix[j] === match[j]
                      ) {
                        j++;
                      }
                      commonPrefix = commonPrefix.slice(0, j);
                    }

                    // 如果共同前缀比当前单词长，补全到共同前缀
                    if (commonPrefix.length > currentWord.length) {
                      const cursor = cm.getCursor();
                      const line = cm.getLine(cursor.line);
                      const textBefore = line.slice(0, cursor.ch);
                      let wordStart = cursor.ch;
                      while (wordStart > 0) {
                        const char = textBefore[wordStart - 1];
                        if (/[\s\(\)\[\]{}\"\']/.test(char)) {
                          break;
                        }
                        wordStart--;
                      }
                      cm.replaceRange(
                        commonPrefix,
                        { line: cursor.line, ch: wordStart },
                        cursor,
                      );
                      return;
                    }

                    // 否则选择第一个匹配项
                    const commonKeywords = [
                      "define",
                      "lambda",
                      "let",
                      "if",
                      "cond",
                      "begin",
                    ];
                    const completion =
                      matches.find((k) => commonKeywords.includes(k)) ||
                      matches[0];
                    const cursor = cm.getCursor();
                    const line = cm.getLine(cursor.line);
                    const textBefore = line.slice(0, cursor.ch);
                    let wordStart = cursor.ch;
                    while (wordStart > 0) {
                      const char = textBefore[wordStart - 1];
                      if (/[\s\(\)\[\]{}\"\']/.test(char)) {
                        break;
                      }
                      wordStart--;
                    }
                    cm.replaceRange(
                      completion,
                      { line: cursor.line, ch: wordStart },
                      cursor,
                    );
                    return;
                  }
                }
                if (cm.somethingSelected()) {
                  cm.indentSelection("add");
                } else {
                  cm.replaceSelection("  ", "end");
                }
              },

              "Ctrl-Enter": (cm) => onEval(),
              "Shift-Enter": function (cm) {
                if (cm.somethingSelected()) {
                  cm.replaceSelection("\n");
                } else {
                  cm.replaceSelection("\n", "end");
                  cm.indentLine(cm.getCursor().line);
                }
              },

              "Ctrl-/": function (cm) {
                const selection = cm.getSelection();
                if (selection) {
                  const lines = selection.split("\n");
                  const commented = lines
                    .map((line) =>
                      line.startsWith(";") ? line.slice(1) : ";" + line,
                    )
                    .join("\n");
                  cm.replaceSelection(commented);
                } else {
                  const cursor = cm.getCursor();
                  const line = cm.getLine(cursor.line);
                  if (line.startsWith(";")) {
                    cm.replaceRange(
                      line.slice(1),
                      { line: cursor.line, ch: 0 },
                      { line: cursor.line, ch: line.length },
                    );
                  } else {
                    cm.replaceRange(
                      ";" + line,
                      { line: cursor.line, ch: 0 },
                      { line: cursor.line, ch: line.length },
                    );
                  }
                }
              },

              "Ctrl-A": function (cm) {
                const lastLine = cm.lastLine();
                const lastLineLength = cm.getLine(lastLine).length;
                cm.setSelection(
                  { line: 0, ch: 0 },
                  { line: lastLine, ch: lastLineLength },
                );
              },
            },
          });
          editor.on("focus", onFocus);
          editor.on("change", () => onChange(editor.getValue()));
          editor.on("keydown", (cm, e) => onHistoryNav(cm, e));

          return editor;
        }

        // ---- Scheme 执行与校验 ----
        async function GoldfishScheme(code) {
          if (typeof Module._eval_string !== "function") {
            throw new Error("WASM模块未正确加载或未导出 _eval_string");
          }
          const wrappedCode = `(begin ${code})`;
          const codePtr = Module.allocateUTF8(wrappedCode);
          const status = Module._eval_string(codePtr);
          let stdout = "",
            stderr = "";
          if (typeof Module._get_out === "function") {
            const outPtr = Module._get_out();
            stdout = Module.UTF8ToString(outPtr);
          }
          if (typeof Module._get_err === "function") {
            const errPtr = Module._get_err();
            stderr = Module.UTF8ToString(errPtr);
          }
          Module._free(codePtr);
          return { stdout, stderr, result: status };
        }
        function isSchemeErrorResult(result) {
          if (!result) return false;
          const errKeywords = [
            "unbound-variable",
            "syntax-error",
            "undefined",
            "error",
            "exception",
            "invalid",
            "not found",
            "wrong type",
            "argument count",
            "未定义",
            "错误",
            "异常",
            "语法",
          ];
          return errKeywords.some((k) =>
            result.toString().toLowerCase().includes(k),
          );
        }
        function schemeLinter(text, editor) {
          clearErrorHighlights(editor);
          let stack = [];
          for (let i = 0; i < text.length; ++i) {
            if (text[i] === "(") stack.push(i);
            if (text[i] === ")") {
              if (stack.length === 0) {
                if (editor && editor.posFromIndex) {
                  const pos = editor.posFromIndex(i);
                  highlightError(editor, pos.line + 1, pos.ch + 1, pos.ch + 2);
                }
              } else {
                stack.pop();
              }
            }
          }
          stack.forEach((idx) => {
            if (editor && editor.posFromIndex) {
              const pos = editor.posFromIndex(idx);
              highlightError(editor, pos.line + 1, pos.ch + 1, pos.ch + 2);
            }
          });
        }
        function clearErrorHighlights(editor) {
          if (editor._errorMarks) {
            editor._errorMarks.forEach((mark) => mark.clear());
          }
          editor._errorMarks = [];
        }
        function highlightError(editor, line, chStart, chEnd) {
          if (!editor._errorMarks) editor._errorMarks = [];
          const mark = editor.markText(
            { line: line - 1, ch: chStart - 1 },
            { line: line - 1, ch: chEnd - 1 },
            { className: "cm-error-highlight" },
          );
          editor._errorMarks.push(mark);
        }

        function isParenMatched(text) {
          let stack = 0;
          for (let i = 0; i < text.length; ++i) {
            if (text[i] === "(") stack++;
            if (text[i] === ")") stack--;
            if (stack < 0) return false;
          }
          return stack === 0;
        }

        // ---- REPL 管理 ----
        const REPL = {
          entryCount: 1,
          activeEditor: null,
          init() {
            const firstEntry = document.querySelector(".repl-entry");
            const container = firstEntry.parentNode;
            firstEntry.outerHTML = this.createEntry(this.entryCount);
            this.setupEntry(container.querySelector(".repl-entry"));
            this.updateHistoryPanel();
          },
          createEntry(number) {
            return `
        <div class="repl-entry">
          <div class="entry-number">#${number}</div>
          <div class="input-area">
            <textarea placeholder="输入Scheme代码..."></textarea>
            <button class="eval-button">求值 <span class="shortcut-hint">(Ctrl+Enter)</span></button>
          </div>
          <div class="output-area"></div>
        </div>
      `.trim();
          },
          setupEntry(entry) {
            const button = entry.querySelector(".eval-button");
            const textarea = entry.querySelector("textarea");
            const output = entry.querySelector(".output-area");
            const editor = createEditor(
              textarea,
              () => button.click(),
              (text) => {
                output.style.display = "none";
                output.innerHTML = "";
                schemeLinter(text, editor);
              },
              () => {
                this.activeEditor = editor;
                History.pointer = null;
              },
              (cm, e) => {
                if (
                  e.key === "ArrowUp" &&
                  !cm.somethingSelected() &&
                  cm.getCursor().line === 0 &&
                  cm.getCursor().ch === 0
                ) {
                  const prev = History.prev();
                  if (prev !== null) {
                    cm.setValue(prev);
                    setTimeout(() => cm.setCursor(cm.lineCount() - 1, 0), 0);
                    e.preventDefault();
                  }
                } else if (e.key === "ArrowDown" && !cm.somethingSelected()) {
                  const next = History.next();
                  cm.setValue(next);
                  setTimeout(() => cm.setCursor(cm.lineCount() - 1, 0), 0);
                  e.preventDefault();
                }
              },
            );
            button.addEventListener("click", async () => {
              try {
                const code = editor.getValue();
                History.pointer = null;
                clearErrorHighlights(editor);

                if (code.trim() === "") {
                  this.displayOutput(output, {
                    stderr: "",
                    stdout: "",
                    result: "",
                  });
                  return;
                }

                if (!isParenMatched(code)) {
                  this.displayOutput(output, {
                    stderr: "括号不匹配，请检查输入。",
                    stdout: "",
                    result: "",
                  });
                  schemeLinter(code, editor);
                  return;
                }

                const evalResult = await GoldfishScheme(code);
                this.displayOutput(output, evalResult);
                if (
                  evalResult.result === 0 &&
                  !evalResult.stderr &&
                  !isSchemeErrorResult(evalResult.stdout)
                ) {
                  History.add(code);
                  this.updateHistoryPanel();
                }
                const allEntries = document.querySelectorAll(".repl-entry");
                const isLastEntry = allEntries[allEntries.length - 1] === entry;
                if (isLastEntry) {
                  this.entryCount++;
                  entry.insertAdjacentHTML(
                    "afterend",
                    this.createEntry(this.entryCount),
                  );
                  this.setupEntry(entry.nextElementSibling);
                  const newEditor =
                    entry.nextElementSibling.querySelector(".CodeMirror");
                  if (newEditor) {
                    newEditor.focus();
                  }
                }
              } catch (e) {
                this.displayOutput(output, {
                  stderr: `错误: ${e.message}`,
                  stdout: "",
                  result: "",
                });
              }
            });
          },
          displayOutput(output, { stderr, stdout, result }) {
            output.style.display = "block";
            output.innerHTML = "";
            if (stderr)
              output.innerHTML += `<div class="error">${stderr}</div>`;
            if (stdout)
              output.innerHTML += `<div class="stdout">${stdout}</div>`;
            if (result)
              output.innerHTML += `<div class="result">${result}</div>`;
          },
          updateHistoryPanel() {
            const list = document.getElementById("history-list");
            History.render(list, (item) => {
              if (this.activeEditor) {
                this.activeEditor.setValue(item);
                this.activeEditor.focus();
              }
            });
          },
          
          updateFavoritesPanel() {
            const list = document.getElementById("favorites-list");
            Favorites.render(list, (item) => {
              if (this.activeEditor) {
                this.activeEditor.setValue(item);
                this.activeEditor.focus();
              }
            });
          },
        };

        // ---- 页面初始化 ----
        document.addEventListener("DOMContentLoaded", () => {
          document
            .querySelectorAll(".eval-button")
            .forEach((btn) => (btn.disabled = true));
          
          if (!History.loadFromURL()) {
            History.loadFromLocal();
          }
          Favorites.loadFromLocal();
        });
        Module.onRuntimeInitialized = () => {
          console.log("goldfish 已加载");
          document
            .querySelectorAll(".eval-button")
            .forEach((btn) => (btn.disabled = false));
          REPL.init();
          REPL.updateHistoryPanel();
          REPL.updateFavoritesPanel();
        };

        document.getElementById('clear-history').onclick = function() {
          const modalMask = document.getElementById('modal-mask');
          const modalDialog = document.getElementById('modal-dialog');
          const modalMessage = document.getElementById('modal-message');
          const modalOk = document.getElementById('modal-ok');
          const modalCancel = document.getElementById('modal-cancel');

          modalMessage.textContent = "确定要清空所有历史吗？";
          modalMask.style.display = 'block';
          modalDialog.style.display = 'block';

          modalOk.onclick = function() {
            History.clear();
            REPL.updateHistoryPanel();
            modalMask.style.display = 'none';
            modalDialog.style.display = 'none';
          };
          modalCancel.onclick = function() {
            modalMask.style.display = 'none';
            modalDialog.style.display = 'none';
          };
        };

        document.getElementById('export-history').onclick = function() {
          const blob = new Blob([History.export()], {type: "application/json"});
          const url = URL.createObjectURL(blob);
          const a = document.createElement('a');
          a.href = url;
          a.download = "goldfish_repl_history.json";
          a.click();
          URL.revokeObjectURL(url);
        };

        document.getElementById('import-history').onclick = function() {
          document.getElementById('import-file').click();
        };
        document.getElementById('import-file').onchange = function(e) {
          const file = e.target.files[0];
          if (!file) return;
          const reader = new FileReader();
          reader.onload = function(evt) {
            if (History.import(evt.target.result)) {
              REPL.updateHistoryPanel();
              alert('历史导入成功');
            } else {
              alert('导入失败，文件格式不正确');
            }
          };
          reader.readAsText(file);
        };

        document.getElementById('share-history').onclick = function() {
          const shareLink = History.generateShareLink();
          if (shareLink) {
            // 创建临时文本区域并自动复制
            const textArea = document.createElement('textarea');
            textArea.value = shareLink;
            textArea.style.position = 'fixed';
            textArea.style.left = '-999999px';
            document.body.appendChild(textArea);
            textArea.select();
            document.execCommand('copy');
            document.body.removeChild(textArea);
            alert('分享链接已复制到剪贴板！');
          } else {
            alert('生成分享链接失败');
          }
        };
      })();
    </script>
    <div id="modal-mask" style="display:none;position:fixed;z-index:2000;left:0;top:0;width:100vw;height:100vh;background:rgba(0,0,0,0.18);"></div>
    <div id="modal-dialog" style="display:none;position:fixed;z-index:2001;left:50%;top:50%;transform:translate(-50%,-50%);background:#fff;border-radius:8px;box-shadow:0 4px 24px rgba(0,0,0,0.18);padding:24px 32px;min-width:260px;text-align:center;">
      <div id="modal-message" style="font-size:16px;color:#44475a;margin-bottom:18px;">确定要清空所有历史吗？</div>
      <button id="modal-ok" style="margin-right:16px;padding:6px 18px;background:#6272a4;color:#fff;border:none;border-radius:4px;cursor:pointer;">确定</button>
      <button id="modal-cancel" style="padding:6px 18px;background:#f1f1f1;color:#44475a;border:1px solid #ddd;border-radius:4px;cursor:pointer;">取消</button>
    </div>
  </body>
</html>
